﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Informaiton_Retrieval.com.ir.utli;

namespace Informaiton_Retrieval_Hw2.com.Utli
{
    class TF_IDFWeightHelper
    {
        IndexDictionary _index;
        public delegate void UpdateHandler(int i);
        public UpdateHandler updateEvent;
        public int Percent { get; private set; }
        List<string> terms;
        void deBugText(string msg)
        {
            Console.WriteLine("deBugText:" + msg);
        }

        public TF_IDFWeightHelper(IndexDictionary index)
        {
            _index = index;
        }
        double lenghtQ;
        int doc_i = 0;

        public List<DocumentScore> GetScores(string[] query)
        {
            List<DocumentScore> list = new List<DocumentScore>();
            terms = UnitTerms(query);
            n = DocNumber(query);
            lenghtQ = DocLenght(query);
            List<String> StringList = DocList(query);
            for (doc_i = 0; doc_i < StringList.Count; doc_i++)
            {
                string docId = StringList[doc_i];
                DocumentScore ds = new DocumentScore();
                ds.DocumentId = docId;
                ds.Score = Score(docId, query);
                if (ds.Score > 0)
                    list.Add(ds);
            }
            list.Sort(new ScoreComparer());
            return list;
        }

        void update(int i, int total)
        {
            int percent = (int)((float)i * 100 / (float)total);
            //if (percent == 0)
            //{ }
            Percent = percent;
            if (updateEvent != null)
                updateEvent(percent);

        }

        private double Score(string docId, string[] query)
        {
            //deBugText("Score:"+docId+","+query);
            double s = 0;
            int DocCount = DocList(query).Count;
            for (int i = 0; i < terms.Count; i++)
            {
                string t = terms[i];
                s += (Normalize(t, docId) * NormalizeQuery(t, query));
                update((i + 1) + (doc_i) * terms.Count, DocCount * terms.Count);
            }
            return s;
        }

        private double Normalize(string term, string docId)//n'lize = wt / length
        {
            //deBugText("Normalize");
            double lenght = DocLenght(docId);
            if (lenght > 0)
                return Wt(term, docId) / lenght;
            return 0;
        }

        private double NormalizeQuery(string term, string[] query)//n'lize = wt / length
        {
            double lenght = lenghtQ;
            if (lenght > 0)
                return Wt(term, query) / lenght;
            return 0;
        }

        private List<string> UnitTerms(string[] query)
        {
            List<string> terms = new List<string>();
            //foreach (string k in _index.Keys)
            //    terms.Add(k);
            foreach (string t in query)
                if (!terms.Contains(t))
                    terms.Add(t);
            return terms;
        }

        private double DocLenght(string docId)//
        {
            List<string> docList = new List<string>();
            double np2 = 0;
            foreach (string key in _index.Keys)
            {
                double wt = Wt(key, docId);
                np2 += wt * wt;
            }
            return Math.Sqrt(np2);
        }

        private double DocLenght(string[] query)//
        {
            List<string> docList = new List<string>();
            double np2 = 0;
            foreach (string key in _index.Keys)
            {
                double wt = Wt(key, query);
                np2 += wt * wt;
            }
            return Math.Sqrt(np2);
        }

        private double DocNumber(string[] query)
        {
            List<string> docList = DocList(query);
            return docList.Count;
        }

        List<string> docList = null;

        private List<string> DocList(string[] query)
        {
            if (docList == null)
            {
                NewMethod2(query);
            }
            return docList;
        }

        private void NewMethod(string[] query)
        {
            docList = new List<string>();
            foreach (string q in query)
            {
                if (_index.ContainsKey(q))
                    foreach (string k in _index[q]._dicPos.Keys)
                        if (!docList.Contains(k))
                            docList.Add(k);
            }
        }

        private void NewMethod2(string[] query)
        {
            docList = new List<string>();
            foreach (string t in _index.Keys)
            {
                foreach (string did in _index[t]._dicPos.Keys) {
                    if (!docList.Contains(did))
                        docList.Add(did);
                }
            }
        }

        private double Wt(string term, string docId)
        {
            double wt = 0;
            double tf = this.Tf(term, docId);
            if (tf > 0)
                wt = (1 + Math.Log10(tf)) * 1;
            //deBugText(term + ",wt:" + wt);
            return wt;
        }
        double n;
        private double Wt(string term, string[] query)
        {
            double wt = 0;
            double tf = this.Tf(term, query);
            double df = this.Df(term);
            if (df == 0)
                return 0;
            if (tf > 0)
                wt = (1 + Math.Log10(tf)) * Math.Log10(n / df);
            return wt;
        }

        private int Tf(string term, string docId)
        {
            if (_index.ContainsKey(term) && _index[term]._dicPos.ContainsKey(docId))
                return _index[term]._dicPos[docId].Count;
            return 0;
        }

        private int Tf(string term, string[] query)
        {
            int i = 0;
            foreach (string s in query)
                if (s == term)
                    i++;
            return i;
        }

        private int Df(string term)
        {
            if (_index.ContainsKey(term))
            {
                return _index[term]._dicPos.Count;
            }

            return 0;
        }
    }
}
